/*******************************************************************************
 * Copyright (c) 2004, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.presentations;

import java.util.ArrayList;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.presentations.IPresentablePart;
import org.eclipse.ui.presentations.IStackPresentationSite;
import org.eclipse.ui.presentations.StackDropResult;
import org.eclipse.ui.presentations.StackPresentation;

/**
 * A stack presentation using native widgets.
 * <p>
 * EXPERIMENTAL
 * </p>
 * 
 */
public class NativeStackPresentation extends StackPresentation {

	private TabFolder tabFolder;

    // RAP [bm]: 
//    private Listener dragListener;

	private IPresentablePart current;

	private MenuManager systemMenuManager = new MenuManager();

	// don't reset this dynamically, so just keep the information static.
	// see bug:
	// 75422 [Presentations] Switching presentation to R21 switches immediately,
	// but only partially
	private static int tabPos = PlatformUI.getPreferenceStore().getInt(
			IWorkbenchPreferenceConstants.VIEW_TAB_POSITION);

	private final static String TAB_DATA = NativeStackPresentation.class.getName() + ".partId"; //$NON-NLS-1$

	private MouseListener mouseListener = new MouseAdapter() {
		public void mouseDown(MouseEvent e) {
			// // PR#1GDEZ25 - If selection will change in mouse up ignore mouse
			// down.
			// // Else, set focus.
			// TabItem newItem = tabFolder.getItem(new Point(e.x, e.y));
			// if (newItem != null) {
			// TabItem oldItem = tabFolder.getSelection();
			// if (newItem != oldItem)
			// return;
			// }
			if (current != null) {
				current.setFocus();
			}
		}
	};

	private Listener menuListener = new Listener() {
		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
		 * .Event)
		 */
		public void handleEvent(Event event) {
			Point pos = new Point(event.x, event.y);
			// FIXME: this method needs work
			
			IPresentablePart part = null;
			// TabItem item = tabFolder.getItem(pos);
			// if (item != null) {
			// part = getPartForTab(item);
			// }
			
			showPaneMenu(part, pos);
		}
	};

	private Listener selectionListener = new Listener() {
		public void handleEvent(Event e) {
			IPresentablePart item = getPartForTab((TabItem) e.item);
			if (item != null) {
				getSite().selectPart(item);
				// item.setFocus();
			}
		}
	};

	private Listener resizeListener = new Listener() {
		public void handleEvent(Event e) {
			setControlSize();
		}
	};

	private IPropertyListener childPropertyChangeListener = new IPropertyListener() {
		public void propertyChanged(Object source, int property) {

			if (isDisposed()) {
				return;
			}

			if (source instanceof IPresentablePart) {
				IPresentablePart part = (IPresentablePart) source;
				childPropertyChanged(part, property);
			}
		}
	};

	private DisposeListener tabDisposeListener = new DisposeListener() {
		public void widgetDisposed(DisposeEvent e) {
			if (e.widget instanceof TabItem) {
				TabItem item = (TabItem) e.widget;
				IPresentablePart part = getPartForTab(item);
				part.removePropertyListener(childPropertyChangeListener);
			}
		}
	};

	public NativeStackPresentation(Composite parent, IStackPresentationSite stackSite) {
		super(stackSite);

		tabFolder = new TabFolder(parent, tabPos);

		// listener to switch between visible tabItems
		tabFolder.addListener(SWT.Selection, selectionListener);

		// listener to resize visible components
		tabFolder.addListener(SWT.Resize, resizeListener);

		// listen for mouse down on tab to set focus.
		tabFolder.addMouseListener(mouseListener);

		tabFolder.addListener(SWT.MenuDetect, menuListener);

		// RAP [bm] DnD
//		dragListener = new Listener() {
//			public void handleEvent(Event event) {
//				// FIXME: this method needs work
//				
//				// Point localPos = new Point(event.x, event.y);
//				// TabItem tabUnderPointer = tabFolder.getItem(localPos);
//				// TabItem tabUnderPointer = null;
//				//
//				// if (tabUnderPointer == null) {
//				// return;
//				// }
//				//
//				// IPresentablePart part = getPartForTab(tabUnderPointer);
//				//
//				// if (getSite().isPartMoveable(part)) {
//				// getSite().dragStart(part, tabFolder.toDisplay(localPos),
//				// false);
//				// }
//			}
//		};
//		PresentationUtil.addDragListener(tabFolder, dragListener);

	}

	/**
	 * Returns the index of the tab for the given part, or returns
	 * tabFolder.getItemCount() if there is no such tab.
	 * 
	 * @param part
	 *            part being searched for
	 * @return the index of the tab for the given part, or the number of tabs if
	 *         there is no such tab
	 */
	private final int indexOf(IPresentablePart part) {
		if (part == null) {
			return tabFolder.getItemCount();
		}

		TabItem[] items = tabFolder.getItems();

		for (int idx = 0; idx < items.length; idx++) {
			IPresentablePart tabPart = getPartForTab(items[idx]);

			if (part == tabPart) {
				return idx;
			}
		}

		return items.length;
	}

	/**
	 * Returns the tab for the given part, or null if there is no such tab
	 * 
	 * @param part
	 *            the part being searched for
	 * @return the tab for the given part, or null if there is no such tab
	 */
	protected final TabItem getTab(IPresentablePart part) {
		TabItem[] items = tabFolder.getItems();

		int idx = indexOf(part);

		if (idx < items.length) {
			return items[idx];
		}

		return null;
	}

    /**
     * @param part
     * @param property
     */
    protected void childPropertyChanged(IPresentablePart part, int property) {
        TabItem tab = getTab(part);
        initTab(tab, part);
    }

    protected final IPresentablePart getPartForTab(TabItem item) {
        IPresentablePart part = (IPresentablePart) item.getData(TAB_DATA);
        return part;
    }

    protected TabFolder getTabFolder() {
        return tabFolder;
    }

    public boolean isDisposed() {
        return tabFolder == null || tabFolder.isDisposed();
    }

    /**
     * Set the size of a page in the folder.
     */
    private void setControlSize() {
        if (current == null || tabFolder == null) {
			return;
		}
		// Rectangle bounds;
		// @issue as above, the mere presence of a theme should not change the
		// behaviour
		// if ((mapTabToPart.size() > 1)
		// || ((tabThemeDescriptor != null) && (mapTabToPart.size() >= 1)))
		// bounds = calculatePageBounds(tabFolder);
		// else
		// bounds = tabFolder.getBounds();
		current.setBounds(calculatePageBounds(tabFolder));
		// current.moveAbove(tabFolder);
	}

	public static Rectangle calculatePageBounds(TabFolder folder) {
		if (folder == null) {
			return new Rectangle(0, 0, 0, 0);
		}
		Rectangle bounds = folder.getBounds();
		Rectangle offset = folder.getClientArea();
		bounds.x += offset.x;
		bounds.y += offset.y;
		bounds.width = offset.width;
		bounds.height = offset.height;
		return bounds;
	}

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.skins.Presentation#dispose()
     */
    public void dispose() {
        if (isDisposed()) {
            return;
        }
        // RAP [bm]: 
//        PresentationUtil.removeDragListener(tabFolder, dragListener);

		// systemMenuManager.dispose();

		tabFolder.dispose();
		tabFolder = null;
	}

	private TabItem createPartTab(IPresentablePart part, int tabIndex) {
		TabItem tabItem = new TabItem(tabFolder, SWT.NONE, tabIndex);
		tabItem.setData(TAB_DATA, part);
		part.addPropertyListener(childPropertyChangeListener);
		tabItem.addDisposeListener(tabDisposeListener);
		initTab(tabItem, part);
		return tabItem;
	}

	/**
	 * Initializes a tab for the given part. Sets the text, icon, tool tip, etc.
	 * This will also be called whenever a relevant property changes in the part
	 * to reflect those changes in the tab. Subclasses may override to change
	 * the appearance of tabs for a particular part.
	 * 
	 * @param tabItem
	 *            tab for the part
	 * @param part
	 *            the part being displayed
	 */
	protected void initTab(TabItem tabItem, IPresentablePart part) {
		tabItem.setText(part.getName());
		tabItem.setToolTipText(part.getTitleToolTip());

		Image tabImage = part.getTitleImage();
		if (tabImage != tabItem.getImage()) {
			tabItem.setImage(tabImage);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.internal.skins.StackPresentation#addPart(org.eclipse.ui
	 * .internal.skins.IPresentablePart,
	 * org.eclipse.ui.internal.skins.IPresentablePart)
	 */
	public void addPart(IPresentablePart newPart, Object cookie) {
		createPartTab(newPart, tabFolder.getItemCount());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.internal.skins.StackPresentation#removePart(org.eclipse
	 * .ui.internal.skins.IPresentablePart)
	 */
	public void removePart(IPresentablePart oldPart) {
		TabItem item = getTab(oldPart);
		if (item == null) {
			return;
		}
		oldPart.setVisible(false);

		item.dispose();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.internal.skins.StackPresentation#selectPart(org.eclipse
	 * .ui.internal.skins.IPresentablePart)
	 */
	public void selectPart(IPresentablePart toSelect) {
		if (toSelect == current) {
			return;
		}

		if (current != null) {
			current.setVisible(false);
		}

		current = toSelect;

		if (current != null) {
			tabFolder.setSelection(indexOf(current));
			current.setVisible(true);
			setControlSize();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.internal.skins.Presentation#setBounds(org.eclipse.swt.
	 * graphics.Rectangle)
	 */
	public void setBounds(Rectangle bounds) {
		tabFolder.setBounds(bounds);
		setControlSize();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.skins.Presentation#computeMinimumSize()
	 */
	public Point computeMinimumSize() {
		return Geometry.getSize(tabFolder.computeTrim(0, 0, 0, 0));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.skins.Presentation#setVisible(boolean)
	 */
	public void setVisible(boolean isVisible) {
		if (current != null) {
			current.setVisible(isVisible);
		}
		tabFolder.setVisible(isVisible);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.skins.Presentation#setState(int)
	 */
	public void setState(int state) {
		// tabFolder.setMinimized(state == IPresentationSite.STATE_MINIMIZED);
		// tabFolder.setMaximized(state == IPresentationSite.STATE_MAXIMIZED);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.skins.Presentation#getSystemMenuManager()
	 */
	public IMenuManager getSystemMenuManager() {
		return systemMenuManager;
	}

	/**
	 * @param part
	 * @param point
	 */
	protected void showPaneMenu(IPresentablePart part, Point point) {
		systemMenuManager.update(false);
		Menu aMenu = systemMenuManager.createContextMenu(tabFolder.getParent());
		aMenu.setLocation(point.x, point.y);
		aMenu.setVisible(true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.internal.skins.Presentation#getControl()
	 */
	public Control getControl() {
		return tabFolder;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.internal.skins.StackPresentation#dragOver(org.eclipse.
	 * swt.widgets.Control, org.eclipse.swt.graphics.Point)
	 */
	public StackDropResult dragOver(Control currentControl, Point location) {
		// FIXME: this method needs work

		// Determine which tab we're currently dragging over
		// Point localPos = tabFolder.toControl(location);
		// final TabItem tabUnderPointer = tabFolder.getItem(localPos);
		// final TabItem tabUnderPointer = null;

		// This drop target only deals with tabs... if we're not dragging over
		// a tab, exit.
		// if (tabUnderPointer == null) {
		// return null;
		// }

		// return new StackDropResult(Geometry.toDisplay(tabFolder,
		// tabUnderPointer.getBounds()),
		// tabFolder.indexOf(tabUnderPointer));
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.presentations.StackPresentation#showSystemMenu()
	 */
	public void showSystemMenu() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.presentations.StackPresentation#showPaneMenu()
	 */
	public void showPaneMenu() {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.presentations.StackPresentation#getTabList(IPresentablePart
	 * )
	 */
	public Control[] getTabList(IPresentablePart part) {
		ArrayList list = new ArrayList();
		if (getControl() != null) {
			list.add(getControl());
		}
		if (part.getToolBar() != null) {
			list.add(part.getToolBar());
		}
		if (part.getControl() != null) {
			list.add(part.getControl());
		}
		return (Control[]) list.toArray(new Control[list.size()]);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.presentations.StackPresentation#getCurrentPart()
	 */
	public IPresentablePart getCurrentPart() {
		return current;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.presentations.StackPresentation#setActive(int)
	 */
	public void setActive(int newState) {

	}

}
